/**
 *  Platypus: Page Layout and Typesetting Software (free at platypus.pz.org)
 *
 *  Platypus is (c) Copyright 2009-12 Pacific Data Works LLC. All Rights Reserved.
 *  Licensed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html)
 */
package org.pz.platypus;

import java.net.*;
import java.lang.reflect.*;

import com.google.common.base.*;
import com.google.common.annotations.*;

import org.pz.platypus.exceptions.*;
import org.pz.platypus.utilities.Filename;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * The principal function for loading and running output plugins.
 *
 * Loads a JAR file plugin and calls the constructor for the class whose name is the output format
 * with a leading capital letter (Fontlist, Pdf). Then calls process() in the same class. Then exits.
 *
 * @author alb
 */
public class PluginLoader
{
    String location;
    GDD gdd;

    public PluginLoader( final String pluginLocation, final GDD Gdd )
    {
        location = pluginLocation;
        gdd =  checkNotNull( Gdd, "GDD passed to PluginLoader.java is null." );
    }

    public void runPlugin()
    {
        URL pluginUrl = createPluginUrl();
        URL[] urls = { pluginUrl };
        URLClassLoader pluginLoader = new URLClassLoader( urls );
        Thread.currentThread().setContextClassLoader( pluginLoader );

        try {
            String className;
            String jarName = checkNotNull( Filename.getBaseName( location ));

            // The class loaded is the name of the JAR with the first letter capitalized pdf.jar -> Pdf.class
            final String capitalizedClass = CaseFormat.LOWER_CAMEL.to( CaseFormat.UPPER_CAMEL, jarName );
            className = "org.pz.platypus.plugin." + jarName.toLowerCase() + "." + capitalizedClass;

            //-- due to bug in JDK 1.6, must use Class.forName(), rather than code above.
            //   see: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6434149
            Class pluginStart = Class.forName( className, false, pluginLoader );

            Object plugin = pluginStart.newInstance();

            Class[] classParams = { GDD.class };
            Method method1;

            // call process( GDD ) in the class identified above.
            try {
                gdd.diag( "Calling process() in:" + " " + className );
                method1 = pluginStart.getMethod( "process", classParams  );
            }
            catch( NoSuchMethodException nsme ) {
                gdd.log.severe( gdd.getLit( "ERROR.INVALID_PLUGIN_NO_PROCESS_METHOD" ));
                throw new StopExecutionException( null );
            }

            try {
                method1.invoke( plugin, gdd );
            }
            catch( InvalidConfigFileException icfe) {
                return; //error message has already been displayed
            }
            catch( InvocationTargetException ite ) {
                System.err.println( "Invocation target exception " + ite );
                ite.printStackTrace();
            }
        }
        catch ( ClassNotFoundException cnf ) {
            gdd.log.severe( "Plugin class not found " + cnf );
            throw new StopExecutionException( null );
        }
        catch ( InstantiationException ie ) {
            System.err.println( ie );
        }
        catch ( IllegalAccessException ie ) {
            System.err.println( ie );
        }
    }

    /**
     * Converts a plugin's JAR file name+address into a URL suitable for class loading
     * @return a valid URL, if all went well.
     */
    @VisibleForTesting
    protected URL createPluginUrl()
    {
        try {
            URL urlForPlugin = new URL( "file:" + location );
            gdd.diag( gdd.getLit( "PLUGIN_URL" ) + " " + urlForPlugin.toString() );
            return ( urlForPlugin );
        }
        catch ( MalformedURLException e ){
            gdd.log.severe(gdd.getLit( "ERROR.INVALID_PLUGIN_URL" ) + ": " + location + "\n" + e);
            throw new StopExecutionException( null );
        }
    }
}
